﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Emgu.CV.Structure;
using Utils;

#endregion

namespace paravili.Steps {
	/// <summary>
	/// Description of the class
	/// </summary>
	public class ConfigurableValuesManager {
		#region Public Properties

		// public Properties located here

		#endregion

		#region Public Methods

		public ConfigurableValuesManager(IProcessStep rootStep) {
			EvaluateStep(rootStep);
		}

		public IList<ConfigurableValueEntry> GetConfigurationValues(IProcessStep step) {
			if (m_items.ContainsKey(step)) {
				return m_items[step].ToList();
			}
			return new List<ConfigurableValueEntry>();
		}

		#endregion

		#region Private Methods

		private void EvaluateStep(IProcessStep step) {
			if (step == null || step is ValueType) {
				return;
			}
			IList<ConfigurableValueEntry> values = FindConfigurableProperties(step);
			m_items.Add(step, new List<ConfigurableValueEntry>());
			m_items[step].AddRange(values);

			IProcessStep subStep = step.GetNextStep();
			if (subStep != null) {
				EvaluateStep(subStep);
			}
		}

		public static IList<ConfigurableValueEntry> FindConfigurableProperties(object inputObject) {
			IList<ConfigurableValueEntry> toReturn = new List<ConfigurableValueEntry>();
			if (inputObject == null) {
				return toReturn;
			}
			// find values
			foreach (PropertyInfo property in inputObject.GetType().GetProperties()) {
				foreach (Attribute attribute in property.GetCustomAttributes(true)) {
					if (attribute is ConfigurableNumericValue) {
						var value = attribute as ConfigurableNumericValue;
						toReturn.Add(new ConfigurableNumericValueEntry {
							Name = value.Name,
							RangeFrom = value.RangeFrom,
							RangeTo = value.RangeTo,
							TargetObject = inputObject,
							Property = property
						});
					}
					else if (attribute is ConfigurableNumericDoubleValue) {
						var value = attribute as ConfigurableNumericDoubleValue;
						toReturn.Add(new ConfigurableNumericDoubleValueEntry {
							Name = value.Name,
							RangeFrom = value.RangeFrom,
							RangeTo = value.RangeTo,
							StepSize = value.StepSize,
							TargetObject = inputObject,
							Property = property
						});
					}
					else if (attribute is ConfigurabelStringValue) {
						var value = attribute as ConfigurabelStringValue;
						toReturn.Add(new ConfigurableStringValueEntry { Name = value.Name, TargetObject = inputObject, Property = property });
					}
					else if (attribute is ConfigurabelBooleanValue) {
						var value = attribute as ConfigurabelBooleanValue;
						toReturn.Add(new ConfigurableBooleanValueEntry { Name = value.Name, TargetObject = inputObject, Property = property });
					}
					else if (attribute is ConfigurableLabValue) {
						var value = attribute as ConfigurableLabValue;
						Lab valueInternal = property.GetValue(inputObject, null) == null
												? new Lab()
												: (Lab)property.GetValue(inputObject, null);
						toReturn.Add(new ConfigurableLabValueEntry {
							Name = value.Name,
							TargetObject = inputObject,
							Property = property,
							X = valueInternal.X,
							Y = valueInternal.Y,
							Z = valueInternal.Z,
						});
					}
				}
			}
			return toReturn;
		}

		#endregion

		#region Private Fields

		private readonly Dictionary<IProcessStep, List<ConfigurableValueEntry>> m_items =
			new Dictionary<IProcessStep, List<ConfigurableValueEntry>>();

		#endregion
	}

	public interface ConfigurableValueEntry {
		Object Value { get; set; }
	}

	public class ConfigurableValueEntry<T> : ConfigurableValueEntry, INotifyPropertyChanged {
		private Object m_targetObject;

		public Object TargetObject {
			get { return m_targetObject; }
			set {
				m_targetObject = value;
				RegisterEvents();
			}
		}

		public PropertyInfo Property { get; set; }

		public virtual T Value {
			get { return (T)Property.GetValue(TargetObject, null); }
			set { Property.SetValue(TargetObject, value, null); }
		}

		public string Name { get; set; }
		private string Description { get; set; }

		#region ConfigurableValueEntry Members

		object ConfigurableValueEntry.Value {
			get { return Value; }
			set { value = (T)value; }
		}

		#endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		private void RegisterEvents() {
			if (TargetObject is INotifyPropertyChanged) {
				(TargetObject as INotifyPropertyChanged).PropertyChanged += OnPropertyChanged;
			}
		}

		private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) {
			if (e.PropertyName == Property.Name) {
				if (PropertyChanged != null) {
					PropertyChanged(this, new PropertyChangedEventArgs("Value"));
				}
			}
		}
	}

	public class ConfigurableNumericValueEntry : ConfigurableValueEntry<int> {
		public int RangeFrom { get; set; }
		public int RangeTo { get; set; }
	}

	public class ConfigurableLabValueEntry : ConfigurableValueEntry<Object> {
		private double m_x;
		private double m_y;
		private double m_z;

		public double X {
			get { return m_x; }
			set {
				m_x = value;
				Value = null;
			}
		}

		public double Y {
			get { return m_y; }
			set {
				m_y = value;
				Value = null;
			}
		}

		public double Z {
			get { return m_z; }
			set {
				m_z = value;
				Value = null;
			}
		}

		public override Object Value {
			get { return (Lab)Property.GetValue(TargetObject, null); }
			set { Property.SetValue(TargetObject, new Lab(X, Y, Z), null); }
		}
	}

	public class ConfigurableNumericDoubleValueEntry : ConfigurableValueEntry<double> {
		public double RangeFrom { get; set; }
		public double RangeTo { get; set; }
		public double StepSize { get; set; }
	}

	public class ConfigurableStringValueEntry : ConfigurableValueEntry<String> {
	}

	public class ConfigurableBooleanValueEntry : ConfigurableValueEntry<Boolean> {
	}
}